/*
 *	Copyright (C) 2008 - Mauricio Bieze Stefani
 *	This file is part of the Insano Game Engine.
 *
 *	Insano is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *
 *	Insano is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with Insano.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "EntityFactory.h"
#include "MBSTrace.h"

using namespace std;
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

void EntityFactory::setIrrlichtDevice(irr::IrrlichtDevice *irrlichtDevice)
{
	if( (device = irrlichtDevice) )
	{
		driver = device->getVideoDriver();
		smgr = device->getSceneManager();
		Entity::smgr = smgr;
	}
}

int EntityFactory::initCreatureLoader(const char *creatureDefinitionXml)
{
	int rc=0;

	// Load file

	if (!creatureDefsXmlDoc.LoadFile(creatureDefinitionXml))
	{
		TRACE_ERROR("TiXmlDocument::LoadFile(%s)", creatureDefinitionXml);
		rc=-1;
	}
	else
	{
		TiXmlHandle hDoc(&creatureDefsXmlDoc);

		// Loop over all filesystem defs

		for(TiXmlElement *fs = hDoc.FirstChild("filesystem").Element(); fs; fs=fs->NextSiblingElement("filesystem"))
		{
			if(NULL==fs->Attribute("file"))
				TRACE_ERROR("Filesystem attribute \'file\' not found");
			else
			{
				const char *filename=fs->Attribute("file");

				if( (NULL==fs->Attribute("type")) || (strcmp(fs->Attribute("type"),"zip")!=0) )
					TRACE_WARN("File %s has invalid \'type\' attribute. Using zip as default", filename);

				if(device->getFileSystem()->addZipFileArchive(filename))
					TRACE_INFO("added %s to virtual filesystem", filename);
				else
				{
					TRACE_ERROR("trying to add %s to virtual filesystem", filename);
					rc=-1;
				}
			}
		}
	}
	return rc;
}

PlayerChar* EntityFactory::CreatePlayerChar(const std::string& creatureName)
{
	PlayerChar *newpc = NULL;

	std::map<std::string,PlayerChar*>::iterator it;

	//search if there is already a copy in the prototype cache
	it = protoPlayerChar.find(creatureName);

	if(it == protoPlayerChar.end())
	{
		newpc = new PlayerChar();

		if(readCreature(creatureName, *newpc) == 0)
		{
			//save it and return a copy
			protoPlayerChar[creatureName] = newpc;
			newpc = newpc->Clone();
		}
		else
		{
			TRACE_ERROR("Error loading character %s", creatureName.c_str());
			delete newpc;
			newpc = NULL;
		}
	}
	else
	{
		newpc = it->second->Clone();
	}
	return newpc;
}

int EntityFactory::readVector(TiXmlElement *parent, const char *propName, irr::core::vector3df& dest)
{
	//all must be valid
	if( (parent==NULL) || (propName==NULL) )
		return -1;

	TiXmlHandle h(parent);
	for(TiXmlElement *el=h.FirstChild("vector3d").Element() ; el ; el=el->NextSiblingElement("vector3d") )
	{
		// is this the one we want?
		if(strcmp(el->Attribute("name"),propName)==0)
		{
			int i;
			char temp[37]; // no reason for this specific size ;-)
			const char *pos = el->Attribute("value");

			if(pos==NULL)
			{
				TRACE_ERROR("could not read %s value", propName);
				return -1;
			}

			if(*pos)
			{
				for(i=0 ; *pos && (*pos != ',') ; i++, pos++)
					temp[i] = *pos;
				if(i)
				{
					temp[i]=0;
					dest.X=atof(temp);
				}
			}

			if(*pos)
			{
				pos++;
				for(i=0 ; *pos && (*pos != ',') ; i++, pos++)
					temp[i] = *pos;
				if(i)
				{
					temp[i]=0;
					dest.Y=atof(temp);
				}
			}

			if(*pos)
			{
				pos++;
				for(i=0 ; *pos && (*pos != ',') ; i++, pos++)
					temp[i] = *pos;
				if(i)
				{
					temp[i]=0;
					dest.Z=atof(temp);
				}
			}
			//TRACE_INFO("readVector(%s, (%f, %f, %f))", propName, dest.X, dest.Y, dest.Z);
			return 0;
		}
	}
	TRACE_ERROR("Unknown vector %s", propName);
	return -1;
}

int EntityFactory::readCreature(const std::string& creatureName, Creature& dest)
{
	TiXmlHandle hDoc(&creatureDefsXmlDoc);

	// loop over all available Creature nodes
	for(TiXmlElement *creatureEl = hDoc.FirstChild("creature").Element() ; creatureEl ; creatureEl = creatureEl->NextSiblingElement("creature"))
	{
		const char *charName = creatureEl->Attribute("name");

		// is this the one we want?
		if((charName) && (strcmp(charName,creatureName.c_str())==0))
		{
			TiXmlHandle h(creatureEl);
			if(TiXmlElement *el=h.FirstChild("mesh").Element())
			{
				const char *file = el->Attribute("file");
				if(file)
				{
					IAnimatedMesh *m = smgr->getMesh(file);
					if(m == NULL)
					{
						TRACE_ERROR("loading mesh %s", file);
						return -1;
					}
					dest.setMesh(m);

					IAnimatedMeshSceneNode *n = smgr->addAnimatedMeshSceneNode(m);
					if (n == NULL)
					{
						TRACE_ERROR("creating scene node from mesh %s", file);
						return -1;
					}
					n->setVisible(false);
					dest.setNode(n);

					if(TiXmlElement *texEl=h.FirstChild("texture").Element())
					{
						const char *texture = texEl->Attribute("file");
						if(texture)
							dest.setTexture(driver->getTexture(texture));
					}

					dest.setAnimation(new AnimationMD2());

					vector3df v3tmp;
					if(readVector(creatureEl, "Scale", v3tmp) == 0)
						dest.setScale(v3tmp);
					if(readVector(creatureEl, "Rotation", v3tmp) == 0)
						dest.setRotation(v3tmp);
					if(readVector(creatureEl, "Position", v3tmp) == 0)
						dest.setPosition(v3tmp);
					if(readVector(creatureEl, "CollisionTranslate", v3tmp) == 0)
						dest.setCollisionTranslate(v3tmp);
					if(readVector(creatureEl, "CameraTranslate", v3tmp) == 0)
						dest.setCameraTranslate(v3tmp);

					return 0;
				}
				else
				{
					TRACE_ERROR("File attribute not found. Creature name = %s", charName);
					return -1;
				}
			}
		}
	}
	TRACE_ERROR("Unknown creature %s", creatureName.c_str());
	return -1;
}
